package online.struts.chain.processor;

import java.io.IOException;
import java.util.Objects;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import online.filter.FilterUtil;
import online.struts.mapping.RequestMapping;

import org.apache.struts.chain.commands.servlet.PerformForward;
import org.apache.struts.chain.contexts.ActionContext;
import org.apache.struts.chain.contexts.ServletActionContext;
import org.apache.struts.config.ActionConfig;
import org.apache.struts.config.ForwardConfig;
import org.apache.struts.util.RequestUtils;

/**
 * フォワード
 *
 * @author Tadashi Nakayama
 */
public final class PerformForwardProcessor extends PerformForward {

	/**
	 * @see org.apache.struts.chain.commands.AbstractPerformForward
	 * #execute(org.apache.struts.chain.contexts.ActionContext)
	 */
	@Override
	public boolean execute(final ActionContext actionCtx) throws Exception {
		if (ServletActionContext.class.isInstance(actionCtx)) {
			ServletActionContext sac = ServletActionContext.class.cast(actionCtx);
			if (sac.getInclude() != null) {
				handleInclude(sac);
				return true;
			}
		}
		return super.execute(actionCtx);
	}

	/**
	 * @see org.apache.struts.chain.commands.servlet.PerformForward
	 * #perform(org.apache.struts.chain.contexts.ActionContext,
	 * org.apache.struts.config.ForwardConfig)
	 */
	@Override
	protected void perform(
			final ActionContext context, final ForwardConfig forwardConfig) throws Exception {
		if (ServletActionContext.class.isInstance(context)) {
			ServletActionContext sac = ServletActionContext.class.cast(context);
			ForwardConfig af = sac.getForwardConfig();
			if (af != null) {
				if (!RequestMapping.isJsp(af) && RequestMapping.isRedirect(af)) {
					ActionConfig ac = sac.getActionConfig();
					if (RequestMapping.class.isInstance(ac)) {
						// リダイレクト(303)
						handleRedirect(sac, RequestMapping.class.cast(ac), af.getPath());
						return;
					}
				} else if (FilterUtil.isInclude(sac.getRequest())) {
					return;
				}
			}
		}

		super.perform(context, forwardConfig);
	}

	/**
	 * リダイレクト
	 * @param sac ServletActionContext
	 * @param mapping マッピング
	 * @param path パス
	 * @throws IOException IO例外
	 */
	private void handleRedirect(final ServletActionContext sac,
			final RequestMapping mapping, final String path) throws IOException {
		sac.getResponse().setStatus(HttpServletResponse.SC_SEE_OTHER);
		sac.getResponse().setHeader("Location", sac.getResponse().encodeRedirectURL(
				getLocation(mapping, sac.getRequest(), path)));
		sac.getResponse().setContentLength(0);
		sac.getResponse().flushBuffer();
	}

	/**
	 * フォワード先取得
	 * @param mapping マッピング
	 * @param request リクエスト
	 * @param path パス
	 * @return フォワード先
	 */
	private String getLocation(final RequestMapping mapping,
			final HttpServletRequest request, final String path) {
		String ret = mapping.addPathInfo(request, path);
		if (ret.startsWith("/")) {
			ret = request.getContextPath() + ret;
		}
		return ret;
	}

	/**
	 * インクルード
	 * @param sac ServletActionContext
	 * @throws IOException IO例外
	 * @throws ServletException Servlet例外
	 */
	private void handleInclude(
			final ServletActionContext sac) throws IOException, ServletException {
		RequestDispatcher rd = sac.getContext().getRequestDispatcher(getLocation(sac));
		rd.include(sac.getRequest(), sac.getResponse());
	}

	/**
	 * インクルード先取得
	 * @param sac ServletActionContext
	 * @return インクルード先
	 */
	private String getLocation(final ServletActionContext sac) {
		String path = RequestUtils.actionIdURL(
				sac.getInclude(), sac.getModuleConfig(), sac.getActionServlet());
		return sac.getModuleConfig().getPrefix() + Objects.toString(path, sac.getInclude());
	}
}
